/*
*********************************************************************************************************
*                                             uC/GUI V3.98
*                        Universal graphic software for embedded applications
*
*                       (c) Copyright 2002, Micrium Inc., Weston, FL
*                       (c) Copyright 2002, SEGGER Microcontroller Systeme GmbH
*
*              �C/GUI is protected by international copyright laws. Knowledge of the
*              source code may not be used to write a similar product. This file may
*              only be used in accordance with a license and should not be redistributed
*              in any way. We appreciate your understanding and fairness.
*
----------------------------------------------------------------------
File        : WIDGET.c
Purpose     : Widget core routines
---------------------------END-OF-HEADER------------------------------
*/

#include <stdlib.h>
//#include <string.h>

#include "WIDGET.h"
#include "GUIDebug.h"
#include "GUI.h"
#include "GUI_Protected.h"
#include "WM_Intern.h"

#if GUI_WINSUPPORT

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

const WIDGET_EFFECT* _pEffectDefault = &WIDGET_Effect_3D;

/*********************************************************************
*
*       Static routines
*
**********************************************************************
*/
/*********************************************************************
*
*       _UpdateChildPositions
*/
static void _UpdateChildPostions(WM_HWIN hObj, I32 Diff)
{
	WM_Obj* pObj;
	pObj = (WM_Obj*)WM_H2P(hObj);
	WM__UpdateChildPositions(pObj, -Diff, -Diff, Diff, Diff);
}

/*********************************************************************
*
*       _EffectRequiresRedraw
*
* Purpose
*   Check if the effect to draw is inside the invalid rectangle.
* Returns:
*   0 if nothing need to be done.
*   1 if the effect needs to be drawn
*/
static I32 _EffectRequiresRedraw(const WIDGET* pWidget, const GUI_RECT * pRect)
{
	I32 EffectSize = pWidget->pEffect->EffectSize;
	GUI_RECT InvalidRect;
	InvalidRect = pWidget->Win.InvalidRect;
	WM__Screen2Client(&pWidget->Win, &InvalidRect);
	/* Check if there a part of the effect is inside the invalid rectangle */
	if ((pRect->x0 + EffectSize) > InvalidRect.x0) {
		return 1;               /* Overlap ... Drawing required */
	}
	if ((pRect->x1 - EffectSize) < InvalidRect.x1) {
		return 1;               /* Overlap ... Drawing required */
	}
	if ((pRect->y0 + EffectSize) > InvalidRect.y0) {
		return 1;               /* Overlap ... Drawing required */
	}
	if ((pRect->y1 - EffectSize) < InvalidRect.y1) {
		return 1;               /* Overlap ... Drawing required */
	}
		return 0;                 /* No overlap ! */
}


/*********************************************************************
*
*       Public routines
*
**********************************************************************
*/
/*********************************************************************
*
*       WIDGET__RotateRect90
*/
void WIDGET__RotateRect90(WIDGET* pWidget, GUI_RECT* pDest, const GUI_RECT* pRect)
{
	I32 x0, x1, XSize;
	x0 = pRect->x0;
	x1 = pRect->x1;
	XSize = pWidget->Win.Rect.x1 - pWidget->Win.Rect.x0;
	pDest->x0 = XSize - pRect->y1;
	pDest->x1 = XSize - pRect->y0;
	pDest->y0 = x0;
	pDest->y1 = x1;

}

/*********************************************************************
*
*       WIDGET__GetClientRect

  Returns the logical client rectangle, which means the normal
  client rectangle for widgets with their standard orientation
  and the rotated one for rotated widgets.
*/
void WIDGET__GetClientRect(WIDGET* pWidget, GUI_RECT* pRect)
{
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		GUI_RECT Rect;
		WM_GetClientRect(&Rect);
		pRect->x0 = Rect.y0;
		pRect->x1 = Rect.y1;
		pRect->y0 = Rect.x0;
		pRect->y1 = Rect.x1;
	} else {
		WM_GetClientRect(pRect);
	}
}

/*********************************************************************
*
*       WIDGET__GetBkColor
*/
GUI_COLOR WIDGET__GetBkColor(WM_HWIN hObj)
{
	GUI_COLOR BkColor = WM_GetBkColor(WM_GetParent(hObj));
	if (BkColor == GUI_INVALID_COLOR) {
		BkColor = DIALOG_GetBkColor();
	}
	return BkColor;
}

/*********************************************************************
*
*       WIDGET__GetInsideRect
*/
void WIDGET__GetInsideRect(WIDGET* pWidget, GUI_RECT* pRect)
{
	WM__GetClientRectWin(&pWidget->Win, pRect);
	GUI__ReduceRect(pRect, pRect, pWidget->pEffect->EffectSize);
}

/*********************************************************************
*
*       WIDGET__GetXSize
*/
I32 WIDGET__GetXSize(const WIDGET* pWidget)
{
	I32 r;
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		r = pWidget->Win.Rect.y1 - pWidget->Win.Rect.y0;
	} else {
		r = pWidget->Win.Rect.x1 - pWidget->Win.Rect.x0;
	}
	return r + 1;
}

/*********************************************************************
*
*       WIDGET__GetYSize
*/
I32 WIDGET__GetYSize(const WIDGET* pWidget)
{
	I32 r;
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		r = pWidget->Win.Rect.x1 - pWidget->Win.Rect.x0;
	} else {
		r = pWidget->Win.Rect.y1 - pWidget->Win.Rect.y0;
	}
	return r + 1;
}

/*******************************************************************
*
*       WIDGET__GetWindowSizeX

  Return width (or height in case of rotation) of window in pixels
*/
I32 WIDGET__GetWindowSizeX(WM_HWIN hWin)
{
	WIDGET* pWidget = WIDGET_H2P(hWin);
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		return WM_GetWindowSizeY(hWin);
	} else {
		return WM_GetWindowSizeX(hWin);
	}
}

/*********************************************************************
*
*       WIDGET_SetState
*/
void WIDGET_SetState(WM_HWIN hObj, I32 State)
{
	WIDGET* pWidget;
	pWidget = WIDGET_H2P(hObj);
	if (State != pWidget->State) {
		pWidget->State = State;
		WM_Invalidate(hObj);
	}
}

/*********************************************************************
*
*       WIDGET_GetState
*/
I32 WIDGET_GetState(WM_HWIN hObj)
{
	I32 Ret = 0;
	WIDGET * pWidget;
	if (hObj) {
		pWidget = WIDGET_H2P(hObj);
		Ret = pWidget->State;
	}
	return Ret;
}

/*********************************************************************
*
*       WIDGET_OrState
*/
void WIDGET_OrState(WM_HWIN hObj, I32 State)
{
	if (hObj) {
		WIDGET* pWidget;
		pWidget = WIDGET_H2P(hObj);
		if (State != (pWidget->State & State)) {
			pWidget->State |= State;
			WM_Invalidate(hObj);
		}
	}
}

/*********************************************************************
*
*       WIDGET_AndState

  Purpose:
    Clear flags in the State element of the widget.
    The bits to be cleared are set.
  Example:
    ...(..., 3);   // Clears bit 0, 1 I32 the state member 

*/
void WIDGET_AndState(WM_HWIN hObj, I32 Mask)
{
	U16 StateNew;
	if (hObj) {
		WIDGET* pWidget;
		pWidget = WIDGET_H2P(hObj);
		StateNew = pWidget->State & (~Mask);
		if (pWidget->State != StateNew) {
			pWidget->State = StateNew;
			WM_Invalidate(hObj);
		}
	}
}

/*********************************************************************
*
*       WIDGET__Init
*/
void WIDGET__Init(WIDGET* pWidget, I32 Id, U16 State)
{
	pWidget->pEffect       = _pEffectDefault;
	pWidget->State         = State;
	pWidget->Id            = Id;
}


/*********************************************************************
*
*       WIDGET_HandleActive
*/
I32 WIDGET_HandleActive(WM_HWIN hObj, WM_MESSAGE* pMsg)
{
	I32 Diff, Notification;
	WIDGET* pWidget = WIDGET_H2P(hObj);
	switch (pMsg->MsgId) {
		case WM_WIDGET_SET_EFFECT:
			Diff = pWidget->pEffect->EffectSize;
			pWidget->pEffect = (const WIDGET_EFFECT*)pMsg->Data.p;
			Diff -= pWidget->pEffect->EffectSize;
			_UpdateChildPostions(hObj, Diff);
			WM_InvalidateWindow(hObj);
		return 0;                        /* Message handled -> Return */
		case WM_GET_ID:
			pMsg->Data.v = pWidget->Id;
		return 0;                        /* Message handled -> Return */
		case WM_PID_STATE_CHANGED:
			if (pWidget->State & WIDGET_STATE_FOCUSSABLE) {
				const WM_PID_STATE_CHANGED_INFO * pInfo = (const WM_PID_STATE_CHANGED_INFO*)pMsg->Data.p;
				if (pInfo->State) {
					WM_SetFocus(hObj);
				}
			}
		break;
		case WM_TOUCH_CHILD:
			/* A descendent (child) has been touched or released.
			If it has been touched, we need to get to top.
			 */
			{
				const WM_MESSAGE * pMsgOrg;
				const GUI_PID_STATE * pState;
				pMsgOrg = (const WM_MESSAGE*)pMsg->Data.p;      /* The original touch message */
				pState = (const GUI_PID_STATE*)pMsgOrg->Data.p;
				if (pState) {          /* Message may not have a valid pointer (moved out) ! */
					if (pState->Pressed) {
						WM_BringToTop(hObj);
						return 0;                    /* Message handled -> Return */
					}
				}
			}
		break;
		case WM_SET_ID:
			pWidget->Id = pMsg->Data.v;
		return 0;                        /* Message handled -> Return */
		case WM_SET_FOCUS:
			if (pMsg->Data.v == 1) {
				WIDGET_SetState(hObj, pWidget->State |  WIDGET_STATE_FOCUS);
				Notification = WM_NOTIFICATION_GOT_FOCUS;
			} else {
				WIDGET_SetState(hObj, pWidget->State & ~WIDGET_STATE_FOCUS);
				Notification = WM_NOTIFICATION_LOST_FOCUS;
			}
			WM_NotifyParent(hObj, Notification);
			//pMsg->Data.v = 0;   /* Focus change accepted */
		return 0;
		case WM_GET_ACCEPT_FOCUS:
			pMsg->Data.v = (pWidget->State & WIDGET_STATE_FOCUSSABLE) ? 1 : 0;               /* Can handle focus */
		return 0;                         /* Message handled */
		case WM_GET_INSIDE_RECT:
			WIDGET__GetInsideRect(pWidget, (GUI_RECT*)pMsg->Data.p);
		return 0;                         /* Message handled */
	}
	return 1;                           /* Message NOT handled */
}

/*********************************************************************
*
*       WIDGET__DrawFocusRect
*/
void WIDGET__DrawFocusRect(WIDGET* pWidget, const GUI_RECT* pRect, I32 Dist)
{
	GUI_RECT Rect;
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		WIDGET__RotateRect90(pWidget, &Rect, pRect);
		pRect = &Rect;
	}
	GUI_DrawFocusRect(pRect, Dist);
}

/*********************************************************************
*
*       WIDGET__DrawVLine
*/
void WIDGET__DrawVLine(WIDGET* pWidget, I32 x, I32 y0, I32 y1)
{
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		GUI_RECT r0, r1;
		r0.x0 = x;
		r0.x1 = x;
		r0.y0 = y0;
		r0.y1 = y1;
		WIDGET__RotateRect90(pWidget, &r1, &r0);
		GUI_DrawHLine(r1.y0, r1.x0, r1.x1);
	} else {
		GUI_DrawVLine(x, y0, y1);
	}
}

/*********************************************************************
*
*       WIDGET__FillRectEx
*/
void WIDGET__FillRectEx(WIDGET* pWidget, const GUI_RECT* pRect)
{
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		GUI_RECT r;
		WIDGET__RotateRect90(pWidget, &r, pRect);
		pRect = &r;
	}
	GUI_FillRectEx(pRect);
}

/*********************************************************************
*
*       WIDGET__EFFECT_DrawDownRect
*/
void WIDGET__EFFECT_DrawDownRect(WIDGET* pWidget, GUI_RECT* pRect)
{
	GUI_RECT Rect;
	if (pRect == NULL) {
		WM_GetClientRect(&Rect);
		pRect = &Rect;
	}
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		WIDGET__RotateRect90(pWidget, &Rect, pRect);
		pRect = &Rect;
	}
	if (_EffectRequiresRedraw(pWidget, pRect)) {
		pWidget->pEffect->pfDrawDownRect(pRect);
	}
}

/*********************************************************************
*
*       WIDGET__EFFECT_DrawDown
*/
void WIDGET__EFFECT_DrawDown(WIDGET* pWidget)
{
	WIDGET__EFFECT_DrawDownRect(pWidget, NULL);
}

/*********************************************************************
*
*       WIDGET__EFFECT_DrawUpRect
*/
void WIDGET__EFFECT_DrawUpRect(WIDGET* pWidget, GUI_RECT* pRect)
{
	GUI_RECT Rect;
	if (pWidget->State & WIDGET_STATE_VERTICAL) {
		WIDGET__RotateRect90(pWidget, &Rect, pRect);
		pRect = &Rect;
	}
	if (_EffectRequiresRedraw(pWidget, pRect)) {
		pWidget->pEffect->pfDrawUpRect(pRect);
	}
}

/*********************************************************************
*
*       WIDGET_SetDefaultEffect
*/
const WIDGET_EFFECT* WIDGET_SetDefaultEffect(const WIDGET_EFFECT* pEffect)
{
	const WIDGET_EFFECT* r;
	r = _pEffectDefault;
	_pEffectDefault = pEffect;
	return r;
}

/*********************************************************************
*
*       WIDGET_GetDefaultEffect
*/
const WIDGET_EFFECT*  WIDGET_GetDefaultEffect(void)
{
	return _pEffectDefault;
}


#else                            /* Avoid problems with empty object modules */
  void WIDGET_C(void) {}
#endif /* GUI_WINSUPPORT */



	 	 			 		    	 				 	  			   	 	 	 	 	 	  	  	      	   		 	 	 		  		  	 		 	  	  			     			       	   	 			  		    	 	     	 				  	 					 	 			   	  	  			 				 		 	 	 			     			 
